#pragma once

#include <iostream>
#include <list>
#include <string>

class IObserver
{
public:
    virtual ~IObserver(){};

public:
    virtual void update(const std::string &message) = 0;
};

class ISubject
{
public:
    virtual ~ISubject(){};

public:
    virtual void attach(IObserver *observer) = 0;
    virtual void detach(IObserver *observer) = 0;
    virtual void notify()                    = 0;
};

class Subject : public ISubject
{
public:
    virtual ~Subject()
    {
        std::cout << "Goodbye, I was the Subject." << std::endl;
    }

public:
    void attach(IObserver *observer) override
    {
        m_listObserver.push_back(observer);
    };

    void detach(IObserver *observer) override
    {
        m_listObserver.remove(observer);
    };

    void notify() override
    {
        auto iter = m_listObserver.begin();
        while (iter != m_listObserver.end()) {
            (*iter)->update(m_message);
            iter++;
        }
    };

    void createMessage(std::string message)
    {
        m_message = message;
        notify();
    }

private:
    std::list<IObserver *> m_listObserver;
    std::string            m_message;
};

class Observer : public IObserver
{
public:
    Observer(Subject &subject) : m_subject(subject)
    {
        this->m_subject.attach(this);
    }
    virtual ~Observer(){};

public:
    void update(const std::string &message) override
    {
        m_message = message;
    }

    void removeMeFromList()
    {
        m_subject.detach(this);
    }

    void printMessage()
    {
        std::cout << m_message << std::endl;
    }

private:
    std::string m_message;
    Subject    &m_subject;
};
